package com.logicbus.backend;

import com.anysoft.util.DefaultProperties;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 服务路径字典树
 *
 * @since 1.6.14.12 [20210617 duanyy]
 */
public class ServicePathTrie<D> {
    protected Node<D> root = new Node<D>("root");
    public ServicePathTrie(){
    }

    public void insert(String path,D data){
        if (StringUtils.isNotEmpty(path)) {
            insert(new ServicePath.Default(path), data);
        }
    }

    public void insert(ServicePath path,D data){
        root.insert(path,0,data);
    }


    public D match(String path,Properties ctx){
        return StringUtils.isNotEmpty(path)?match(new ServicePath.Default(path),ctx):null;
    }

    public D match(ServicePath path,Properties ctx){
        return root.find(path,0,ctx);
    }

    public static class Node<D> {
        protected String id;
        protected D data = null;
        protected List<Node> children = new ArrayList<Node>();
        protected List<Node> children2 = new ArrayList<Node>();
        protected Pattern pattern = Pattern.compile("\\{(.*)\\}");

        public Node(String id){
            this.id = id;
        }

        public String getId(){
            return id;
        }

        public synchronized void insert(ServicePath path,int current,D data){
            List<String> list = path.list();
            if (current < 0 || current >= list.size()){
                this.data = data;
                return ;
            }
            boolean isPathVariable = false;
            String id = list.get(current);
            Matcher matcher = pattern.matcher(id);
            if (matcher.find()){
                id = matcher.group(1);
                isPathVariable = true;
            }

            if (isPathVariable) {
                Node found = getChild(children2,id);
                if (found == null) {
                    found = new Node(id);
                    children2.add(found);
                }
                found.insert(path, current + 1, data);
            }else{
                Node found = getChild(children,id);
                if (found == null) {
                    found = new Node(id);
                    children.add(found);
                }
                found.insert(path, current + 1, data);
            }
        }

        protected Node getChild(List<Node> nodes,String id){
            for (Node node:nodes){
                if (node.getId().equals(id)){
                    return node;
                }
            }
            return null;
        }

        public D find(ServicePath path, int current, Properties ctx){
            List<String> list = path.list();
            if (current < 0 || current >= list.size()){
                return data;
            }
            String value = list.get(current);
            for (Node<D> child : children) {
                if (!value.equals(child.getId())) {
                    continue;
                }
                D found = child.find(path, current + 1, ctx);
                if (found != null) {
                    return found;
                }
            }
            for (Node<D> child : children2) {
                D found = child.find(path, current + 1, ctx);
                if (found != null) {
                    ctx.SetValue(child.getId(), value);
                    return found;
                }
            }
            return null;
        }
    }

    public static void main(String[] args){
        ServicePathTrie<String> trie = new ServicePathTrie<String>();
        trie.insert("/v1/domain/{domain}/state","/domain/state");
        trie.insert("/v1/domain/{domain}","/domain");
        trie.insert("/v1/domain/info","/domain/info");

        Properties ctx = new DefaultProperties();
        String found = trie.match("/v1/domain/vip.ctcdn.cn/state",ctx);
        if (found != null){
            System.out.println(found);
            System.out.println(PropertiesConstants.getString(ctx,"domain","0",true));
        }
        found = trie.match("/v1/domain/vip-dev.ctcdn.cn",ctx);
        if (found != null){
            System.out.println(found);
            System.out.println(PropertiesConstants.getString(ctx,"domain","0",true));
        }
        found = trie.match("/v1/domain/info",ctx);
        if (found != null){
            System.out.println(found);
        }
    }
}
